{
  "cells": [
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "R2Aw6UeAAA0j"
      },
      "source": [
        "\n",
        " Numpy\n",
        "  - Vectorization\n",
        "  - Boardcast\n",
        "  - Identify Matrix\n",
        "  - Indexing and Slicing\n",
        "  - Fancy Array\n",
        "  - Reduction Operation"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "sm2ka4lprn1-"
      },
      "source": [
        "# Basic Section (Start)\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "ekC5lhEacpKu"
      },
      "outputs": [],
      "source": [
        "import numpy as np"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "BJ0zt3V0cpKv"
      },
      "source": [
        "## Numpy (Review)\n",
        "\n",
        "Main object type is `np.array`\n",
        "\n",
        "Many ways to create it,\n",
        "\n",
        "One way is to convert a python list"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 3,
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "id": "xIO3lTj-cpKx",
        "outputId": "a3a6657f-05eb-4f46-e053-78ce932cf3a2"
      },
      "outputs": [
        {
          "data": {
            "text/plain": [
              "array([1, 2, 3])"
            ]
          },
          "execution_count": 3,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "python_list = [1, 2, 3]\n",
        "arr = np.array(python_list)\n",
        "arr"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 4,
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "id": "pc4i2a416Z5M",
        "outputId": "96388ab6-3a8e-46fa-e7bf-b8e7bb047869"
      },
      "outputs": [
        {
          "data": {
            "text/plain": [
              "array([1, 4, 9])"
            ]
          },
          "execution_count": 4,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "arr**2"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 5,
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/",
          "height": 163
        },
        "id": "LDX_ymhAKp8I",
        "outputId": "4987a17a-ef5a-49db-d477-634527aa6217"
      },
      "outputs": [
        {
          "ename": "TypeError",
          "evalue": "unsupported operand type(s) for ** or pow(): 'list' and 'int'",
          "output_type": "error",
          "traceback": [
            "\u001b[31m---------------------------------------------------------------------------\u001b[39m",
            "\u001b[31mTypeError\u001b[39m                                 Traceback (most recent call last)",
            "\u001b[36mCell\u001b[39m\u001b[36m \u001b[39m\u001b[32mIn[5]\u001b[39m\u001b[32m, line 1\u001b[39m\n\u001b[32m----> \u001b[39m\u001b[32m1\u001b[39m \u001b[43mpython_list\u001b[49m\u001b[43m*\u001b[49m\u001b[43m*\u001b[49m\u001b[32;43m2\u001b[39;49m \u001b[38;5;66;03m#__pow__\u001b[39;00m\n",
            "\u001b[31mTypeError\u001b[39m: unsupported operand type(s) for ** or pow(): 'list' and 'int'"
          ]
        }
      ],
      "source": [
        "python_list**2 #__pow__"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 58,
      "metadata": {
        "id": "cEIaZrB0US0q"
      },
      "outputs": [],
      "source": [
        "# class.function(self, args) = instance.function(args)"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 59,
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/",
          "height": 198
        },
        "id": "2yasABN8J8z8",
        "outputId": "2eff2da7-aa4d-4cb7-994f-65d2d56d623d"
      },
      "outputs": [
        {
          "ename": "AttributeError",
          "evalue": "'list' object has no attribute '__pow__'",
          "output_type": "error",
          "traceback": [
            "\u001b[31m---------------------------------------------------------------------------\u001b[39m",
            "\u001b[31mAttributeError\u001b[39m                            Traceback (most recent call last)",
            "\u001b[36mCell\u001b[39m\u001b[36m \u001b[39m\u001b[32mIn[59]\u001b[39m\u001b[32m, line 3\u001b[39m\n\u001b[32m      1\u001b[39m \u001b[38;5;66;03m# Magic Function with __XXX__\u001b[39;00m\n\u001b[32m      2\u001b[39m \u001b[38;5;66;03m# arr.__pow__(2)\u001b[39;00m\n\u001b[32m----> \u001b[39m\u001b[32m3\u001b[39m \u001b[43mpython_list\u001b[49m\u001b[43m.\u001b[49m\u001b[34;43m__pow__\u001b[39;49m(\u001b[32m2\u001b[39m)\n",
            "\u001b[31mAttributeError\u001b[39m: 'list' object has no attribute '__pow__'"
          ]
        }
      ],
      "source": [
        "# Magic Function with __XXX__\n",
        "# arr.__pow__(2)\n",
        "python_list.__pow__(2)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "Du3Lpmi-cpKz"
      },
      "source": [
        "Many times a list comprehension is used to create a list and then converted to a array"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "id": "4Xz0VGT0cpKz",
        "outputId": "6cfd53a6-6809-4629-a5d9-052c8a781c53"
      },
      "outputs": [],
      "source": [
        "python_list_pow = [i**2 for i in python_list]  # list comprehension\n",
        "python_list_pow"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 47,
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "id": "aFu-SnfbMnfk",
        "outputId": "813428f1-286c-453e-96e8-d2a6f07d9cb1"
      },
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "Original list: [1, 2, 3]\n",
            "I am called __pow___ for powerlist\n",
            "List to the power of 2: [1, 4, 9]\n"
          ]
        }
      ],
      "source": [
        "# Extend the behavior of built-in class use inheritance\n",
        "# not directly changing it\n",
        "class PowerList(list):\n",
        "    def __pow__(self, exponent):\n",
        "        print(\"I am called __pow___ for powerlist\")\n",
        "        if not isinstance(exponent, int):\n",
        "            raise ValueError(\"Exponent must be an integer\")\n",
        "        return [i**2 for i in self]\n",
        "\n",
        "my_list = PowerList()\n",
        "my_list.extend([1,2,3])\n",
        "print(\"Original list:\", my_list)\n",
        "\n",
        "powered_list = my_list ** 2\n",
        "print(\"List to the power of 2:\", powered_list)\n"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "U1acF_mOcpK2"
      },
      "source": [
        "### Exercise (Pre-Lec)\n",
        "Create a numpy array that contain  intergers i  such that  0<i<100 and $2^i$ has the last digit 6"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 6,
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "id": "1B0gVXMSbsoU",
        "outputId": "7bb5b155-e2a3-4c3b-98cd-eb07e2631ae5"
      },
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "[ 4  8 12 16 20 24 28 32 36 40 44 48 52 56 60 64 68 72 76 80 84 88 92 96]\n"
          ]
        }
      ],
      "source": [
        "mask1 = [pow(2, i)%10 == 6 for i in range(1, 100)]\n",
        "result1 = np.array([i for i in range(1, 100) if mask1[i-1]])\n",
        "print(result1)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "c0fMYyztcpK3"
      },
      "source": [
        "Create a 2D numpy array $A$ (5,10) such that $A_{ij} = i\\times j$"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 73,
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "id": "ObzFUe3GbtCk",
        "outputId": "005909c0-52e2-4b56-ed1d-68d25b32cc52"
      },
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "[[ 0.  0.  0.  0.  0.  0.  0.  0.  0.  0.]\n",
            " [ 0.  1.  2.  3.  4.  5.  6.  7.  8.  9.]\n",
            " [ 0.  2.  4.  6.  8. 10. 12. 14. 16. 18.]\n",
            " [ 0.  3.  6.  9. 12. 15. 18. 21. 24. 27.]\n",
            " [ 0.  4.  8. 12. 16. 20. 24. 28. 32. 36.]]\n"
          ]
        }
      ],
      "source": [
        "result2=np.ones(shape=(5,10))\n",
        "for i in range(0,5):\n",
        "    for j in range(0,10):\n",
        "        result2[i,j]=i*j\n",
        "print(result2)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "Clx3TzpdcpK7"
      },
      "source": [
        "## Another way to create a numpy array is with initializing functions\n",
        "\n",
        "- np.zeros\n",
        "- np.ones\n",
        "- np.arange\n",
        "\n",
        "These functions along with `reshape` can be used to create initial matrix without any for loops"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 75,
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "id": "Q_MicPfVcpK7",
        "outputId": "c805d4ab-47c2-402f-fd1c-7340f079c174"
      },
      "outputs": [
        {
          "data": {
            "text/plain": [
              "array([[0., 0., 0., 0., 0., 0., 0., 0., 0., 0.],\n",
              "       [0., 0., 0., 0., 0., 0., 0., 0., 0., 0.],\n",
              "       [0., 0., 0., 0., 0., 0., 0., 0., 0., 0.],\n",
              "       [0., 0., 0., 0., 0., 0., 0., 0., 0., 0.],\n",
              "       [0., 0., 0., 0., 0., 0., 0., 0., 0., 0.],\n",
              "       [0., 0., 0., 0., 0., 0., 0., 0., 0., 0.],\n",
              "       [0., 0., 0., 0., 0., 0., 0., 0., 0., 0.],\n",
              "       [0., 0., 0., 0., 0., 0., 0., 0., 0., 0.],\n",
              "       [0., 0., 0., 0., 0., 0., 0., 0., 0., 0.],\n",
              "       [0., 0., 0., 0., 0., 0., 0., 0., 0., 0.]])"
            ]
          },
          "execution_count": 75,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "np.zeros(shape = (10, 10))"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 76,
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "id": "ewrnqEk0cpK8",
        "outputId": "78065a9f-0034-4f09-e59d-5e422f9df4b4"
      },
      "outputs": [
        {
          "data": {
            "text/plain": [
              "array([[2., 2., 2., 2., 2., 2., 2., 2., 2., 2.],\n",
              "       [2., 2., 2., 2., 2., 2., 2., 2., 2., 2.],\n",
              "       [2., 2., 2., 2., 2., 2., 2., 2., 2., 2.],\n",
              "       [2., 2., 2., 2., 2., 2., 2., 2., 2., 2.],\n",
              "       [2., 2., 2., 2., 2., 2., 2., 2., 2., 2.],\n",
              "       [2., 2., 2., 2., 2., 2., 2., 2., 2., 2.],\n",
              "       [2., 2., 2., 2., 2., 2., 2., 2., 2., 2.],\n",
              "       [2., 2., 2., 2., 2., 2., 2., 2., 2., 2.],\n",
              "       [2., 2., 2., 2., 2., 2., 2., 2., 2., 2.],\n",
              "       [2., 2., 2., 2., 2., 2., 2., 2., 2., 2.]])"
            ]
          },
          "execution_count": 76,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "np.ones((10, 10)) * 2"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 77,
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "id": "9OvLSdfdcpK8",
        "outputId": "1d51ff1b-108b-4b7c-8315-dde5eb920041"
      },
      "outputs": [
        {
          "data": {
            "text/plain": [
              "array([2, 4, 6, 8])"
            ]
          },
          "execution_count": 77,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "np.arange(2, 10, 2)  # equivalent to range(2,10,2)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "wkfUmrQOQt-C"
      },
      "source": [
        "### Exercise (In Lec)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "yxz7GZEicpK6"
      },
      "source": [
        "Create an array of first 10 powers of 2"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 7,
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "id": "UQr37aElWsSl",
        "outputId": "f7298b51-36c4-456a-c207-be65aeea2396"
      },
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "[  1   2   4   8  16  32  64 128 256 512]\n"
          ]
        }
      ],
      "source": [
        "result3=np.power(2, np.arange(10))\n",
        "print(result3)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "KBuHu7Mng2Y5"
      },
      "source": [
        "### 1D vs 2D array"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 8,
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "id": "1a1g17LScpK9",
        "outputId": "663ada13-60ac-415c-cf67-ccd58a19ad7b"
      },
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "(1, 10)\n"
          ]
        },
        {
          "data": {
            "text/plain": [
              "array([[ 0,  1,  4,  9, 16, 25, 36, 49, 64, 81]])"
            ]
          },
          "execution_count": 8,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "array1D = np.arange(10) * np.arange(10)\n",
        "array1D = array1D.reshape(1,-1)\n",
        "print(array1D.shape)\n",
        "array1D"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 9,
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "id": "TEkHHkPVlcta",
        "outputId": "a3950493-1261-4b6a-cba1-6ae91b1417e0"
      },
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "(10, 1)\n"
          ]
        },
        {
          "data": {
            "text/plain": [
              "array([[0],\n",
              "       [1],\n",
              "       [2],\n",
              "       [3],\n",
              "       [4],\n",
              "       [5],\n",
              "       [6],\n",
              "       [7],\n",
              "       [8],\n",
              "       [9]])"
            ]
          },
          "execution_count": 9,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "array2D = np.arange(10).reshape(10, 1)\n",
        "print(array2D.shape)\n",
        "array2D"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 10,
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "id": "n9MqPp9WcpK9",
        "outputId": "adce5ad1-a57f-4cff-80cf-c63c1828e275"
      },
      "outputs": [
        {
          "data": {
            "text/plain": [
              "array([[ 0,  1,  4,  9, 16, 25, 36, 49, 64, 81],\n",
              "       [ 1,  2,  5, 10, 17, 26, 37, 50, 65, 82],\n",
              "       [ 2,  3,  6, 11, 18, 27, 38, 51, 66, 83],\n",
              "       [ 3,  4,  7, 12, 19, 28, 39, 52, 67, 84],\n",
              "       [ 4,  5,  8, 13, 20, 29, 40, 53, 68, 85],\n",
              "       [ 5,  6,  9, 14, 21, 30, 41, 54, 69, 86],\n",
              "       [ 6,  7, 10, 15, 22, 31, 42, 55, 70, 87],\n",
              "       [ 7,  8, 11, 16, 23, 32, 43, 56, 71, 88],\n",
              "       [ 8,  9, 12, 17, 24, 33, 44, 57, 72, 89],\n",
              "       [ 9, 10, 13, 18, 25, 34, 45, 58, 73, 90]])"
            ]
          },
          "execution_count": 10,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "array2D + array1D #boardcasting"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "4m52F0GLcpK-"
      },
      "source": [
        "### Distinction between numpy 1D arrays and numpy 2D arrays\n",
        "\n",
        "This tends to cause a lot of confusion for new numpy users.\n",
        "Follow the below examples carefully to understand the distinction."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 84,
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "id": "hOS5NmDccpK_",
        "outputId": "6ce460bf-af49-48bd-e53f-1da6e4c79b6f"
      },
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "[0. 0. 0. 0. 0. 0. 0. 0. 0. 0.]\n"
          ]
        },
        {
          "data": {
            "text/plain": [
              "(10,)"
            ]
          },
          "execution_count": 84,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "Z = np.zeros(shape=10)\n",
        "print(Z)\n",
        "Z.shape"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 85,
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "id": "ORpO8ZaFcpK_",
        "outputId": "60d8475e-bb50-4c9b-ddc5-65477e1d1ef1"
      },
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "[[0.]\n",
            " [0.]\n",
            " [0.]\n",
            " [0.]\n",
            " [0.]\n",
            " [0.]\n",
            " [0.]\n",
            " [0.]\n",
            " [0.]\n",
            " [0.]]\n"
          ]
        },
        {
          "data": {
            "text/plain": [
              "(10, 1)"
            ]
          },
          "execution_count": 85,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "# Create 2D array by reshape\n",
        "Z = np.zeros(10).reshape(10, 1)\n",
        "print(Z)\n",
        "Z.shape"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 86,
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "id": "-AiQ3VrVl2hN",
        "outputId": "930012e2-9905-460f-9e2d-fcbe4a138b39"
      },
      "outputs": [
        {
          "data": {
            "text/plain": [
              "array([0., 0., 0., 0., 0., 0., 0., 0., 0., 0.])"
            ]
          },
          "execution_count": 86,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "Z.squeeze()  # remove axis with length = 1"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 88,
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "id": "wtG-DpHAcpK_",
        "outputId": "d92f6c08-ceb3-4aa9-fb3f-4abe7c285165"
      },
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "[[[[0. 0. 0.]\n",
            "   [0. 0. 0.]]]]\n",
            "(1, 1, 2, 3) \n",
            "\n",
            "[[0. 0. 0.]\n",
            " [0. 0. 0.]]\n",
            "(2, 3)\n"
          ]
        }
      ],
      "source": [
        "# squeeze Remove axes of length one\n",
        "Z = np.zeros(6).reshape(1, 1, 2, 3)\n",
        "print(Z)\n",
        "print(Z.shape, \"\\n\")\n",
        "\n",
        "Z_squeeze = Z.squeeze()\n",
        "print(Z_squeeze)\n",
        "print(Z_squeeze.shape)"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 87,
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "id": "BonXzKqucpLA",
        "outputId": "efd149ed-520e-434e-8be9-a0d1072aee72"
      },
      "outputs": [
        {
          "data": {
            "text/plain": [
              "(10, 10)"
            ]
          },
          "execution_count": 87,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "# Matrix Multiplication\n",
        "Mat = np.random.randn(10, 10)\n",
        "Mat.shape"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 89,
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "id": "Zgge9PBYcpLA",
        "outputId": "ee3abf21-11a4-4b83-dc38-d552684db8be"
      },
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "[[0]\n",
            " [1]\n",
            " [2]\n",
            " [3]\n",
            " [4]\n",
            " [5]\n",
            " [6]\n",
            " [7]\n",
            " [8]\n",
            " [9]]\n",
            "(10, 1)\n"
          ]
        }
      ],
      "source": [
        "Z = np.arange(10).reshape(10, 1)\n",
        "print(Z)\n",
        "print(Z.shape)"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 101,
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "id": "4PzWzG4Att7w",
        "outputId": "d259eea7-a09b-48f4-b0ee-cdda26dd8625"
      },
      "outputs": [
        {
          "data": {
            "text/plain": [
              "array([[ 14.25537992],\n",
              "       [-15.04112614],\n",
              "       [  0.12922216],\n",
              "       [-16.12712711],\n",
              "       [-23.64818532],\n",
              "       [  0.44453848],\n",
              "       [ 16.91987423],\n",
              "       [ -5.02770268],\n",
              "       [-21.41111931],\n",
              "       [-11.69103106]])"
            ]
          },
          "execution_count": 101,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "# (N, M) @ (M, K) = (N, K)\n",
        "Mat @ Z"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/",
          "height": 251
        },
        "id": "wNeR6q8mcpLA",
        "outputId": "a08924f2-cd15-4e5d-b375-8fa88d9e52c3"
      },
      "outputs": [],
      "source": [
        "Z = np.arange(10).reshape(1, 10)\n",
        "\n",
        "print(Mat.shape)\n",
        "print(Z.shape)\n",
        "\n",
        "# (N, M) @ (M, K) = (N, K)\n",
        "Mat @ Z  # (10, 10) @ (1, 10) NOT WORKING"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "id": "csqE3Sqq1_YX",
        "outputId": "06c51cf0-9c8a-437f-9059-fa2d25f4eb37"
      },
      "outputs": [],
      "source": [
        "(Z @ Mat).shape"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 90,
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "id": "WxkZh6mtcpLB",
        "outputId": "d32a7424-aa8d-48ca-8a21-7c0e982a015e"
      },
      "outputs": [
        {
          "data": {
            "text/plain": [
              "array([[0., 0., 0., 0., 0.],\n",
              "       [0., 0., 0., 0., 0.],\n",
              "       [0., 0., 0., 0., 0.],\n",
              "       [0., 0., 0., 0., 0.],\n",
              "       [0., 0., 0., 0., 0.]])"
            ]
          },
          "execution_count": 90,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "# array variable is also a pointer\n",
        "x = np.zeros((5, 5))\n",
        "y = x.copy()\n",
        "x[1, 1] = 2\n",
        "\n",
        "y"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "6F8S5BDUcpLB"
      },
      "source": [
        "## Array Broadcasting\n",
        "\n",
        "Normally you only do arithmetic operations between arrays of the same dimension\n",
        "\n",
        "The smaller array of at least 1 dimension of size 1 is “broadcast” across the larger array so that they have compatible shapes by dimension."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 92,
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "id": "Odab6zqGMpLE",
        "outputId": "d71981e2-73a5-4e89-f31c-5e718881143d"
      },
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "[[0 1 2]] \n",
            "\n",
            "[[0]\n",
            " [1]\n",
            " [2]\n",
            " [3]\n",
            " [4]\n",
            " [5]] \n",
            "\n",
            "[[1. 1. 1.]\n",
            " [1. 1. 1.]\n",
            " [1. 1. 1.]] \n",
            "\n",
            "[[0. 0. 0.]\n",
            " [0. 0. 0.]\n",
            " [0. 0. 0.]\n",
            " [0. 0. 0.]\n",
            " [0. 0. 0.]\n",
            " [0. 0. 0.]] \n",
            "\n"
          ]
        }
      ],
      "source": [
        "a = np.arange(3).reshape(1, 3)\n",
        "b = np.arange(6).reshape(6, 1)\n",
        "c = np.ones((3, 3))\n",
        "d = np.zeros((6, 3))\n",
        "print(a, \"\\n\")\n",
        "print(b, \"\\n\")\n",
        "print(c, \"\\n\")\n",
        "print(d, \"\\n\")"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 93,
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "id": "UEsPlv7KcpLC",
        "outputId": "b039743a-7c42-4439-b1b1-ae0dfd524ed8"
      },
      "outputs": [
        {
          "data": {
            "text/plain": [
              "array([[0., 0., 0.],\n",
              "       [1., 1., 1.],\n",
              "       [2., 2., 2.],\n",
              "       [3., 3., 3.],\n",
              "       [4., 4., 4.],\n",
              "       [5., 5., 5.]])"
            ]
          },
          "execution_count": 93,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "# c + 2\n",
        "# c + c\n",
        "# d + a\n",
        "# c + d\n",
        "b + d"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 94,
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "id": "YRiRURBPcpLD",
        "outputId": "347347e1-4d8e-406f-f24d-c0e35aa70ef8"
      },
      "outputs": [
        {
          "data": {
            "text/plain": [
              "array([[0, 1, 2],\n",
              "       [1, 2, 3],\n",
              "       [2, 3, 4],\n",
              "       [3, 4, 5],\n",
              "       [4, 5, 6],\n",
              "       [5, 6, 7]])"
            ]
          },
          "execution_count": 94,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "a + b"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 95,
      "metadata": {
        "id": "S0rfUgFtcpLD"
      },
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "(4, 1)\n",
            "(1, 3) \n",
            "\n",
            "[[ 0]\n",
            " [10]\n",
            " [20]\n",
            " [30]] \n",
            "\n",
            "[[0 1 2]] \n",
            "\n"
          ]
        },
        {
          "data": {
            "text/plain": [
              "array([[ 0,  1,  2],\n",
              "       [10, 11, 12],\n",
              "       [20, 21, 22],\n",
              "       [30, 31, 32]])"
            ]
          },
          "execution_count": 95,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "X = np.arange(4).reshape(-1, 1) * 10\n",
        "Y = np.arange(3).reshape(1, -1)\n",
        "print(X.shape)\n",
        "print(Y.shape, \"\\n\")\n",
        "print(X, \"\\n\")\n",
        "print(Y, \"\\n\")\n",
        "X + Y"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "8Hv2qdGpluQl"
      },
      "source": [
        "it tries to duplicates every x’s col and y’s row (dim size = 1) to match the other arrays\n",
        "1. Make the two arrays have the same number of dimensions.\n",
        "  - If the numbers of dimensions of the two arrays are different, add new dimensions with size 1 to the head of the array with the smaller dimension.\n",
        "\n",
        "2. If there is a dimension whose size is not 1 in either of the two arrays, it cannot be broadcasted, and an error is raised."
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "MhdH-qrJcpLD"
      },
      "source": [
        "![](http://scipy-lectures.org/_images/numpy_broadcasting.png)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "2ls4P9gDcpLD"
      },
      "source": [
        "### Exercise (In-Lecture)\n",
        "create a 2D numpy array $A$ (shape = (5,10) ) such that $A_{ij} = i\\times j$, but without using list comprehensions. Use broadcasting instead\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 11,
      "metadata": {
        "id": "4irE8JMJvArT"
      },
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "[[ 0  0  0  0  0  0  0  0  0  0]\n",
            " [ 0  1  2  3  4  5  6  7  8  9]\n",
            " [ 0  2  4  6  8 10 12 14 16 18]\n",
            " [ 0  3  6  9 12 15 18 21 24 27]\n",
            " [ 0  4  8 12 16 20 24 28 32 36]]\n"
          ]
        }
      ],
      "source": [
        "# hint: check how it looks for\n",
        "#       np.arange(5).reshape(-1,1)\n",
        "#       np.arange(10).reshape(1,-1)\n",
        "mul1=np.arange(5).reshape(-1,1)\n",
        "mul2=np.arange(10).reshape(1,-1)\n",
        "result4=mul1*mul2\n",
        "print(result4)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "v4e7Hp77cpLE"
      },
      "source": [
        "Use array broadcasting to create a (10,10) numpy array with values\n",
        "$$ A_{ij} = 2^i + j $$"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 12,
      "metadata": {
        "id": "JAmO02Z-nDlT"
      },
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "[[  1   2   3   4   5   6   7   8   9  10]\n",
            " [  2   3   4   5   6   7   8   9  10  11]\n",
            " [  4   5   6   7   8   9  10  11  12  13]\n",
            " [  8   9  10  11  12  13  14  15  16  17]\n",
            " [ 16  17  18  19  20  21  22  23  24  25]\n",
            " [ 32  33  34  35  36  37  38  39  40  41]\n",
            " [ 64  65  66  67  68  69  70  71  72  73]\n",
            " [128 129 130 131 132 133 134 135 136 137]\n",
            " [256 257 258 259 260 261 262 263 264 265]\n",
            " [512 513 514 515 516 517 518 519 520 521]]\n"
          ]
        }
      ],
      "source": [
        "# Hint: Check the values of\n",
        "#       2**(np.arange(10).reshape(-1,1))\n",
        "#       np.arange(10).reshape(1,-1)\n",
        "add1=2**(np.arange(10).reshape(-1,1))\n",
        "add2= np.arange(10).reshape(1,-1)\n",
        "result5=add1+add2\n",
        "print(result5)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "TKaxKNLns6vy"
      },
      "source": [
        "# Basic Section (End)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "SuT-q0hKcpLE"
      },
      "source": [
        "## Matrix creation\n",
        "\n",
        "There are some functions to create standard matrices"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 6,
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "id": "m_AduRywcpLE",
        "outputId": "e36d59fb-4b10-4c8d-f826-bd0885a90c91"
      },
      "outputs": [
        {
          "data": {
            "text/plain": [
              "array([[1., 0., 0., 0., 0.],\n",
              "       [0., 1., 0., 0., 0.],\n",
              "       [0., 0., 1., 0., 0.],\n",
              "       [0., 0., 0., 1., 0.],\n",
              "       [0., 0., 0., 0., 1.]])"
            ]
          },
          "execution_count": 6,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "np.eye(5)"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 7,
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "id": "ik1RQuxs4xaQ",
        "outputId": "621d9cf4-751b-4d20-fcf3-9ac3f7acf426"
      },
      "outputs": [
        {
          "data": {
            "text/plain": [
              "array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])"
            ]
          },
          "execution_count": 7,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "np.arange(10)"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 8,
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "id": "bc0OrhdycpLE",
        "outputId": "d877ba9d-f8c4-4e43-883d-faa57d4ccc40"
      },
      "outputs": [
        {
          "data": {
            "text/plain": [
              "array([[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],\n",
              "       [0, 1, 0, 0, 0, 0, 0, 0, 0, 0],\n",
              "       [0, 0, 2, 0, 0, 0, 0, 0, 0, 0],\n",
              "       [0, 0, 0, 3, 0, 0, 0, 0, 0, 0],\n",
              "       [0, 0, 0, 0, 4, 0, 0, 0, 0, 0],\n",
              "       [0, 0, 0, 0, 0, 5, 0, 0, 0, 0],\n",
              "       [0, 0, 0, 0, 0, 0, 6, 0, 0, 0],\n",
              "       [0, 0, 0, 0, 0, 0, 0, 7, 0, 0],\n",
              "       [0, 0, 0, 0, 0, 0, 0, 0, 8, 0],\n",
              "       [0, 0, 0, 0, 0, 0, 0, 0, 0, 9]])"
            ]
          },
          "execution_count": 8,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "# np.diag: Extract a diagonal or construct a diagonal array.\n",
        "M = np.diag(np.arange(10))  # .reshape(5,20)\n",
        "M"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 11,
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "id": "CCH9_P_0cpLF",
        "outputId": "c5b32583-4edc-4356-e6ac-909775aca514"
      },
      "outputs": [
        {
          "data": {
            "text/plain": [
              "array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])"
            ]
          },
          "execution_count": 11,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "np.diag(M)"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 10,
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "id": "p6xq39cCcpLF",
        "outputId": "c99b1be9-7a92-4d5c-913f-1b072fe6c77b"
      },
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "(5, 3)\n"
          ]
        },
        {
          "data": {
            "text/plain": [
              "array([[ 0,  1,  2],\n",
              "       [ 3,  4,  5],\n",
              "       [ 6,  7,  8],\n",
              "       [ 9, 10, 11],\n",
              "       [12, 13, 14]])"
            ]
          },
          "execution_count": 10,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "# Transpose of the matrix\n",
        "A = np.arange(15).reshape(5, 3)\n",
        "print(A.shape)\n",
        "A"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 98,
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "id": "5MCDBv15jDy9",
        "outputId": "02f697d8-ff25-4885-8d0f-2657ecfe4657"
      },
      "outputs": [
        {
          "data": {
            "text/plain": [
              "array([[ 0,  3,  6,  9, 12],\n",
              "       [ 1,  4,  7, 10, 13],\n",
              "       [ 2,  5,  8, 11, 14]])"
            ]
          },
          "execution_count": 98,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "A.T"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 99,
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "id": "xrExcBTqcpLF",
        "outputId": "779b46f3-109f-4c91-b7c1-5394ddd3ae81"
      },
      "outputs": [
        {
          "data": {
            "text/plain": [
              "array([[ 0,  3,  6,  9, 12],\n",
              "       [ 1,  4,  7, 10, 13],\n",
              "       [ 2,  5,  8, 11, 14]])"
            ]
          },
          "execution_count": 99,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "A.transpose()"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "Aa_FofGIoIyQ"
      },
      "source": [
        "### random seed"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 100,
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "id": "uqSzWqbaoulP",
        "outputId": "d39e8170-2e5a-4b06-b44d-2af4b3aef20b"
      },
      "outputs": [
        {
          "data": {
            "text/plain": [
              "array([[0.47215885, 0.57149934, 0.16729554, 0.82331322, 0.81196924],\n",
              "       [0.03834529, 0.31146229, 0.47009787, 0.33719555, 0.59366774],\n",
              "       [0.53096008, 0.99047684, 0.42342411, 0.65348958, 0.63689323],\n",
              "       [0.67735957, 0.4458741 , 0.52648795, 0.82051055, 0.95364305],\n",
              "       [0.9640667 , 0.88697778, 0.58634193, 0.90184274, 0.96306094]])"
            ]
          },
          "execution_count": 100,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "np.random.rand(5, 5)"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "id": "G6gxAaVhcpLG",
        "outputId": "657bef4b-e404-44c4-e22f-dfa0d2948b38"
      },
      "outputs": [],
      "source": [
        "np.random.seed(0)  # control the random state\n",
        "print(np.random.rand(5, 5))\n",
        "print(np.random.rand(5, 5))\n",
        "print(np.random.rand(5, 5))"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "c32AfJCMcpLG"
      },
      "source": [
        "### Exercise (Post Lecture)\n",
        "\n",
        "Create this matrix   \n",
        "\n",
        "```python\n",
        "array([[5., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1.],\n",
        "       [1., 4., 1., 1., 1., 1., 1., 1., 1., 1., 1.],\n",
        "       [1., 1., 3., 1., 1., 1., 1., 1., 1., 1., 1.],\n",
        "       [1., 1., 1., 2., 1., 1., 1., 1., 1., 1., 1.],\n",
        "       [1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1.],\n",
        "       [1., 1., 1., 1., 1., 0., 1., 1., 1., 1., 1.],\n",
        "       [1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1.],\n",
        "       [1., 1., 1., 1., 1., 1., 1., 2., 1., 1., 1.],\n",
        "       [1., 1., 1., 1., 1., 1., 1., 1., 3., 1., 1.],\n",
        "       [1., 1., 1., 1., 1., 1., 1., 1., 1., 4., 1.],\n",
        "       [1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 5.]])\n",
        "```"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 13,
      "metadata": {
        "id": "H0546O8UcpLH"
      },
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "[[5. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1.]\n",
            " [1. 4. 1. 1. 1. 1. 1. 1. 1. 1. 1.]\n",
            " [1. 1. 3. 1. 1. 1. 1. 1. 1. 1. 1.]\n",
            " [1. 1. 1. 2. 1. 1. 1. 1. 1. 1. 1.]\n",
            " [1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1.]\n",
            " [1. 1. 1. 1. 1. 0. 1. 1. 1. 1. 1.]\n",
            " [1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1.]\n",
            " [1. 1. 1. 1. 1. 1. 1. 2. 1. 1. 1.]\n",
            " [1. 1. 1. 1. 1. 1. 1. 1. 3. 1. 1.]\n",
            " [1. 1. 1. 1. 1. 1. 1. 1. 1. 4. 1.]\n",
            " [1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 5.]]\n"
          ]
        }
      ],
      "source": [
        "ones=np.ones(shape=(11,11))\n",
        "rows, cols = np.diag_indices_from(ones)\n",
        "for i in rows:\n",
        "    if i<=5 :\n",
        "        ones[i,i]=5-i\n",
        "    else:\n",
        "        ones[i,i]=i-5\n",
        "print(ones)\n"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "7sBUB-10cpLH"
      },
      "source": [
        "## Array Indexing and Slicing"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 107,
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "id": "w_fypLsicpLH",
        "outputId": "68f01b74-0eba-48d3-b4a0-3701c456d06e"
      },
      "outputs": [
        {
          "data": {
            "text/plain": [
              "array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])"
            ]
          },
          "execution_count": 107,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "import numpy as np\n",
        "\n",
        "arr = np.arange(10)\n",
        "arr"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 114,
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "id": "ek7TJqX-cpLH",
        "outputId": "e303b571-71e4-473a-ce77-4d3718cd0f93"
      },
      "outputs": [
        {
          "data": {
            "text/plain": [
              "np.int64(5)"
            ]
          },
          "execution_count": 114,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "arr[5]"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 111,
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "id": "3VwM84XPRCIV",
        "outputId": "36d05a5e-801d-49e8-8401-50d91a7e41d8"
      },
      "outputs": [
        {
          "data": {
            "text/plain": [
              "np.int64(7)"
            ]
          },
          "execution_count": 111,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "arr[-3]"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 19,
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "id": "WsRvDLWacpLI",
        "outputId": "66705a6f-c169-48a8-c9b4-4a582a94154f"
      },
      "outputs": [
        {
          "data": {
            "text/plain": [
              "array([3, 4, 5, 6])"
            ]
          },
          "execution_count": 19,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "arr[3:7]"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 20,
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "id": "DH2tZoZ5cpLI",
        "outputId": "4e341fd3-3d23-4685-8e1c-0e914ada873f"
      },
      "outputs": [
        {
          "data": {
            "text/plain": [
              "array([2, 3, 4, 5, 6, 7, 8, 9])"
            ]
          },
          "execution_count": 20,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "arr[2:]"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 21,
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "id": "7OASWxJncpLI",
        "outputId": "04d837ca-5e51-40ca-846b-73c4c99c9a5a"
      },
      "outputs": [
        {
          "data": {
            "text/plain": [
              "array([0, 1, 2, 3, 4, 5, 6])"
            ]
          },
          "execution_count": 21,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "arr[0:-3]"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 22,
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "id": "yjg5vIYbcpLI",
        "outputId": "fe8de7fe-a196-4dab-81f8-18eb5958e4e4"
      },
      "outputs": [
        {
          "data": {
            "text/plain": [
              "array([0, 2, 4])"
            ]
          },
          "execution_count": 22,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "arr[0:6:2]  # similar as range(0,6,2)"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 23,
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "id": "YvNXaLtPcpLI",
        "outputId": "dde1ed19-1e8d-4745-984a-1d92ec9de2e7"
      },
      "outputs": [
        {
          "data": {
            "text/plain": [
              "array([5, 3, 1])"
            ]
          },
          "execution_count": 23,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "arr[5:0:-2]"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 24,
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "id": "woqqKnphcpLI",
        "outputId": "6fb240e4-2a34-4dfd-d8ff-80a2e6758e20"
      },
      "outputs": [
        {
          "data": {
            "text/plain": [
              "array([9, 8, 7, 6, 5, 4, 3, 2, 1, 0])"
            ]
          },
          "execution_count": 24,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "arr[::-1]"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 25,
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "id": "SbWOYU1rcpLJ",
        "outputId": "c3682b41-bc15-4285-ca76-a711c3fa9289"
      },
      "outputs": [
        {
          "data": {
            "text/plain": [
              "array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])"
            ]
          },
          "execution_count": 25,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "arr[:]"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 26,
      "metadata": {
        "id": "SUQJIQgakNze"
      },
      "outputs": [
        {
          "data": {
            "text/plain": [
              "array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])"
            ]
          },
          "execution_count": 26,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "arr"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 120,
      "metadata": {
        "id": "8cIHEi6Mn_Sf"
      },
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "[[ 0  1  2  3  4  5]\n",
            " [10 11 12 13 14 15]\n",
            " [20 21 22 23 24 25]\n",
            " [30 31 32 33 34 35]\n",
            " [40 41 42 43 44 45]\n",
            " [50 51 52 53 54 55]]\n"
          ]
        }
      ],
      "source": [
        "a = 10 * np.arange(6).reshape(-1, 1) + np.arange(6)\n",
        "print(a)\n",
        "# a[4:, 4:]"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "tapilH8YcpLJ"
      },
      "source": [
        "Can use all the above slicing methods for each dimension of a multidemnsional array\n",
        "![](http://scipy-lectures.org/_images/numpy_indexing.png)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "4tUcmolfcpLJ"
      },
      "source": [
        "try it yourself"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "y-mTwEvkcpLK"
      },
      "source": [
        "### Exercise (Post-Lecture)\n",
        "Create the following matrix, using `np.ones()`, `np.zeros()`, and slicing\n",
        "```python\n",
        "array([[1., 1., 1., 1., 1., 1., 1., 1., 1., 1.],\n",
        "       [1., 1., 1., 1., 1., 1., 1., 1., 1., 1.],\n",
        "       [1., 1., 0., 0., 0., 0., 0., 0., 1., 1.],\n",
        "       [1., 1., 0., 0., 0., 0., 0., 0., 1., 1.],\n",
        "       [1., 1., 0., 0., 0., 0., 0., 0., 1., 1.],\n",
        "       [1., 1., 0., 0., 0., 0., 0., 0., 1., 1.],\n",
        "       [1., 1., 0., 0., 0., 0., 0., 0., 1., 1.],\n",
        "       [1., 1., 0., 0., 0., 0., 0., 0., 1., 1.],\n",
        "       [1., 1., 1., 1., 1., 1., 1., 1., 1., 1.],\n",
        "       [1., 1., 1., 1., 1., 1., 1., 1., 1., 1.]])\n",
        "```"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 117,
      "metadata": {
        "id": "-kXUXbGRcpLK"
      },
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "[[1. 1. 1. 1. 1. 1. 1. 1. 1. 1.]\n",
            " [1. 1. 1. 1. 1. 1. 1. 1. 1. 1.]\n",
            " [1. 1. 0. 0. 0. 0. 0. 0. 1. 1.]\n",
            " [1. 1. 0. 0. 0. 0. 0. 0. 1. 1.]\n",
            " [1. 1. 0. 0. 0. 0. 0. 0. 1. 1.]\n",
            " [1. 1. 0. 0. 0. 0. 0. 0. 1. 1.]\n",
            " [1. 1. 0. 0. 0. 0. 0. 0. 1. 1.]\n",
            " [1. 1. 0. 0. 0. 0. 0. 0. 1. 1.]\n",
            " [1. 1. 1. 1. 1. 1. 1. 1. 1. 1.]\n",
            " [1. 1. 1. 1. 1. 1. 1. 1. 1. 1.]]\n"
          ]
        }
      ],
      "source": [
        "ones=np.ones(shape=(10,10))\n",
        "ones[2:8,2:8]=0\n",
        "print(ones)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "UHi8Zj4acpLK"
      },
      "source": [
        "Create the following matrix\n",
        "```python\n",
        "array([[-1., -1., -1., -1., -1., -1., -1., -1., -1., -1.],\n",
        "       [-1.,  0.,  1.,  2.,  3.,  4., -1., -1., -1., -1.],\n",
        "       [-1.,  5.,  6.,  7.,  8.,  9., -1., -1., -1., -1.],\n",
        "       [-1., 10., 11., 12., 13., 14., -1., -1., -1., -1.],\n",
        "       [-1., 15., 16., 17., 18., 19., -1., -1., -1., -1.],\n",
        "       [-1., 20., 21., 22., 23., 24., -1., -1., -1., -1.],\n",
        "       [-1., 25., 26., 27., 28., 29., -1., -1., -1., -1.],\n",
        "       [-1., 30., 31., 32., 33., 34., -1., -1., -1., -1.],\n",
        "       [-1., 35., 36., 37., 38., 39., -1., -1., -1., -1.],\n",
        "       [-1., -1., -1., -1., -1., -1., -1., -1., -1., -1.]])\n",
        "```"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 124,
      "metadata": {
        "id": "OfG1CN9FcpLK"
      },
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "[[-1. -1. -1. -1. -1. -1. -1. -1. -1. -1.]\n",
            " [-1.  0.  1.  2.  3.  4. -1. -1. -1. -1.]\n",
            " [-1.  5.  6.  7.  8.  9. -1. -1. -1. -1.]\n",
            " [-1. 10. 11. 12. 13. 14. -1. -1. -1. -1.]\n",
            " [-1. 15. 16. 17. 18. 19. -1. -1. -1. -1.]\n",
            " [-1. 20. 21. 22. 23. 24. -1. -1. -1. -1.]\n",
            " [-1. 25. 26. 27. 28. 29. -1. -1. -1. -1.]\n",
            " [-1. 30. 31. 32. 33. 34. -1. -1. -1. -1.]\n",
            " [-1. 35. 36. 37. 38. 39. -1. -1. -1. -1.]\n",
            " [-1. -1. -1. -1. -1. -1. -1. -1. -1. -1.]]\n"
          ]
        }
      ],
      "source": [
        "mones=np.ones(shape=(10,10))*-1\n",
        "mones[1:9,1:6]+=np.arange(1,41).reshape(8,5)\n",
        "print(mones)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "r1a-dglxcpLL"
      },
      "source": [
        "# Fancy Array Indexing\n",
        "\n",
        "We can use numpy arrays as an index for other numpy arrays"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 14,
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "id": "Q8xYPQexcpLL",
        "outputId": "5f4c02c6-067d-4263-e7b7-20345877193d"
      },
      "outputs": [
        {
          "data": {
            "text/plain": [
              "array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])"
            ]
          },
          "execution_count": 14,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "arr = np.arange(10)\n",
        "arr"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 15,
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "id": "wccPNb_KcpLL",
        "outputId": "53db96b9-ec27-4238-8638-2401ffe590bf"
      },
      "outputs": [
        {
          "data": {
            "text/plain": [
              "array([ 2,  7, -1])"
            ]
          },
          "execution_count": 15,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "# use array/list/tuple as indexs\n",
        "idx = np.array([2, 7, -1])\n",
        "idx"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 27,
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "id": "_b6P6mzecpLL",
        "outputId": "7d2be830-92c1-430a-8439-10b20c1fdf39"
      },
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "[2 7 9]\n"
          ]
        },
        {
          "data": {
            "text/plain": [
              "array([ 0,  1, -1,  3,  4,  5,  6, -1,  8, -1])"
            ]
          },
          "execution_count": 27,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "print(arr[idx])\n",
        "arr[idx] = -1\n",
        "arr"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 28,
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "id": "0u7cOoKVcpLL",
        "outputId": "530c9468-39ae-4641-d6a4-9f5b710314b2"
      },
      "outputs": [
        {
          "data": {
            "text/plain": [
              "array([False, False,  True, False, False, False, False,  True, False,\n",
              "        True])"
            ]
          },
          "execution_count": 28,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "# use bool array\n",
        "arr < 0"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 29,
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "id": "CBSA1MaOcpLL",
        "outputId": "ae133335-2a82-41ce-c210-3733756a2817"
      },
      "outputs": [
        {
          "data": {
            "text/plain": [
              "array([  0,   1, 100,   3,   4,   5,   6, 100,   8, 100])"
            ]
          },
          "execution_count": 29,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "arr[arr < 0] = 100\n",
        "arr"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "ieEw3imtcpLM"
      },
      "source": [
        "For multidimensional array, array indexing works different from slicing"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 30,
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "id": "gLwyjAAtcpLM",
        "outputId": "d4b02dce-c55a-4df7-e220-1f9f52ad4487"
      },
      "outputs": [
        {
          "data": {
            "text/plain": [
              "array([[0., 0., 0., 0., 0., 0.],\n",
              "       [0., 0., 0., 0., 0., 0.],\n",
              "       [1., 1., 1., 0., 0., 0.],\n",
              "       [1., 1., 1., 0., 0., 0.],\n",
              "       [1., 1., 1., 0., 0., 0.],\n",
              "       [0., 0., 0., 0., 0., 0.]])"
            ]
          },
          "execution_count": 30,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "X = np.zeros((6, 6))\n",
        "X[2:5, 0:3] = 1\n",
        "X"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 31,
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "id": "OgszejT3ky5c",
        "outputId": "f9fc711f-3970-4d70-dbf5-e2365759f1b5"
      },
      "outputs": [
        {
          "data": {
            "text/plain": [
              "(array([2, 3, 4]), array([0, 1, 2]))"
            ]
          },
          "execution_count": 31,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "np.arange(2, 5), np.arange(0, 3)"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 32,
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "id": "dryNdm5PcpLM",
        "outputId": "8a55a100-6ff3-4369-bced-62cf84789d1e"
      },
      "outputs": [
        {
          "data": {
            "text/plain": [
              "array([[0., 0., 0., 0., 0., 0.],\n",
              "       [0., 0., 0., 0., 0., 0.],\n",
              "       [1., 0., 0., 0., 0., 0.],\n",
              "       [0., 1., 0., 0., 0., 0.],\n",
              "       [0., 0., 1., 0., 0., 0.],\n",
              "       [0., 0., 0., 0., 0., 0.]])"
            ]
          },
          "execution_count": 32,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "X = np.zeros((6, 6))\n",
        "X[np.arange(2, 5), np.arange(0, 3)] = 1\n",
        "X"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 17,
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "id": "T3cfJVoecpLM",
        "outputId": "762b1a62-47fa-4caf-8975-b9c98303af58"
      },
      "outputs": [
        {
          "data": {
            "text/plain": [
              "array([[ 0,  1,  2,  3,  4,  5],\n",
              "       [10, 11, 12, 13, 14, 15],\n",
              "       [20, 21, 22, 23, 24, 25],\n",
              "       [30, 31, 32, 33, 34, 35],\n",
              "       [40, 41, 42, 43, 44, 45],\n",
              "       [50, 51, 52, 53, 54, 55]])"
            ]
          },
          "execution_count": 17,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "# Here is our array, what should we return?\n",
        "a = 10 * np.arange(6).reshape(-1, 1) + np.arange(6)\n",
        "a"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 34,
      "metadata": {
        "id": "gYPf1JVrcpLN"
      },
      "outputs": [
        {
          "data": {
            "text/plain": [
              "array([10, 21, 32, 43, 54])"
            ]
          },
          "execution_count": 34,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "a[(1, 2, 3, 4, 5), (0, 1, 2, 3, 4)]"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 18,
      "metadata": {
        "id": "En0_DPUATTD5"
      },
      "outputs": [
        {
          "data": {
            "text/plain": [
              "array([[30, 32, 35],\n",
              "       [40, 42, 45],\n",
              "       [50, 52, 55]])"
            ]
          },
          "execution_count": 18,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "a[3:, [0, 2, 5]]"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 36,
      "metadata": {
        "id": "SF6L76_uTTMb"
      },
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "[ True False  True False False  True]\n"
          ]
        },
        {
          "data": {
            "text/plain": [
              "array([ 2, 22, 52])"
            ]
          },
          "execution_count": 36,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "mask = np.array([1, 0, 1, 0, 0, 1], dtype=bool)\n",
        "print(mask)\n",
        "a[mask, 2]"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 38,
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "id": "vToQqdO-dwy9",
        "outputId": "d4f43cd8-ccc5-4fec-aaba-ccb43957c9a0"
      },
      "outputs": [
        {
          "data": {
            "text/plain": [
              "array([12,  2, 12,  2,  2, 12])"
            ]
          },
          "execution_count": 38,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "mask = np.array([1,0,1,0,0,1])\n",
        "a[mask,2]"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "40etmVYRcpLN"
      },
      "source": [
        "![](http://scipy-lectures.org/_images/numpy_fancy_indexing.png)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "yjacL27WcpLN"
      },
      "source": [
        "### Exercise (Post - Lecture)\n",
        "Create the following matrix\n",
        "```python\n",
        "array([[0., 0., 0., 0., 0., 0., 0., 0., 0., 1.],\n",
        "       [0., 0., 0., 0., 0., 0., 0., 1., 0., 0.],\n",
        "       [0., 0., 0., 0., 0., 1., 0., 0., 0., 0.],\n",
        "       [0., 0., 0., 1., 0., 0., 0., 0., 0., 0.],\n",
        "       [0., 1., 0., 0., 0., 0., 0., 0., 0., 0.],\n",
        "       [0., 1., 0., 0., 0., 0., 0., 0., 0., 0.],\n",
        "       [0., 0., 0., 1., 0., 0., 0., 0., 0., 0.],\n",
        "       [0., 0., 0., 0., 0., 1., 0., 0., 0., 0.],\n",
        "       [0., 0., 0., 0., 0., 0., 0., 1., 0., 0.],\n",
        "       [0., 0., 0., 0., 0., 0., 0., 0., 0., 1.]])\n",
        "```"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 14,
      "metadata": {
        "id": "DTkKnYepcpLO"
      },
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "[[0. 0. 0. 0. 0. 0. 0. 0. 0. 1.]\n",
            " [0. 0. 0. 0. 0. 0. 0. 1. 0. 0.]\n",
            " [0. 0. 0. 0. 0. 1. 0. 0. 0. 0.]\n",
            " [0. 0. 0. 1. 0. 0. 0. 0. 0. 0.]\n",
            " [0. 1. 0. 0. 0. 0. 0. 0. 0. 0.]\n",
            " [0. 0. 0. 1. 0. 0. 0. 0. 0. 0.]\n",
            " [0. 0. 0. 0. 0. 1. 0. 0. 0. 0.]\n",
            " [0. 0. 0. 0. 0. 0. 0. 1. 0. 0.]\n",
            " [0. 0. 0. 0. 0. 0. 0. 0. 0. 1.]\n",
            " [0. 0. 0. 0. 0. 0. 0. 0. 0. 0.]]\n"
          ]
        }
      ],
      "source": [
        "cols=np.array([9,7,5,3,1,3,5,7,9])\n",
        "rows=np.array([0,1,2,3,4,5,6,7,8])\n",
        "result6=np.zeros(shape=(10,10))\n",
        "result6[rows,cols]=1\n",
        "print(result6)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "nTu3gNhNcpLO"
      },
      "source": [
        "### Exercise (Post Lecture)\n",
        "Write a function to compute the [trace](https://en.wikipedia.org/wiki/Trace_(linear_algebra)) of a square numpy array using fancy array indexing. Compare your implementation to numpy's built-in function `np.trace`."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 19,
      "metadata": {
        "id": "1ZE8dfvbcpLO"
      },
      "outputs": [
        {
          "data": {
            "text/plain": [
              "np.True_"
            ]
          },
          "execution_count": 19,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "def trace(a):\n",
        "  l=a.shape[1]\n",
        "  ans=0\n",
        "  for i in range(l):\n",
        "    ans+=a[i,i]\n",
        "  return ans\n",
        "#a=np.array([[1,2,3],[4,5,6],[7,8,9]])  \n",
        "#print(trace(a))\n",
        "trace(a) == np.trace(a)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "q5zRB9GycpLO"
      },
      "source": [
        "We can use `np.where`, to get indices of the `True` values in a boolean array"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 137,
      "metadata": {
        "id": "-Z454xw1uWhr"
      },
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "[[ 0  1  2  3  4]\n",
            " [ 5  6  7  8  9]\n",
            " [10 11 12 13 14]\n",
            " [15 16 17 18 19]\n",
            " [20 21 22 23 24]]\n",
            "(array([3, 3, 3, 3, 3, 4, 4, 4, 4, 4]), array([0, 1, 2, 3, 4, 0, 1, 2, 3, 4]))\n"
          ]
        }
      ],
      "source": [
        "Y = np.arange(25).reshape(5, 5)\n",
        "print(Y)\n",
        "Y > 14\n",
        "print(np.where(Y > 14))\n",
        "# Y[np.where(Y>14)]"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "QLxyEUlJcpLO"
      },
      "source": [
        "## Reduction operations\n",
        "\n",
        "Many reduction functions are available\n",
        "\n",
        "- np.sum, np.prod\n",
        "- np.min, np.max\n",
        "- np.any, np.all\n",
        "\n",
        "Partial reductions\n",
        "\n",
        "- np.cumsum, np.cumprod"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 138,
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "id": "n3-oTPmAcpLP",
        "outputId": "62cb0415-cd2a-452a-b2e5-6f78deb09347"
      },
      "outputs": [
        {
          "data": {
            "text/plain": [
              "array([[ 0,  1,  2,  3,  4],\n",
              "       [ 5,  6,  7,  8,  9],\n",
              "       [10, 11, 12, 13, 14],\n",
              "       [15, 16, 17, 18, 19],\n",
              "       [20, 21, 22, 23, 24],\n",
              "       [25, 26, 27, 28, 29],\n",
              "       [30, 31, 32, 33, 34],\n",
              "       [35, 36, 37, 38, 39],\n",
              "       [40, 41, 42, 43, 44],\n",
              "       [45, 46, 47, 48, 49]])"
            ]
          },
          "execution_count": 138,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "X = np.arange(50).reshape(10,5)\n",
        "X"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 139,
      "metadata": {
        "id": "JlHMkXQKcpLP"
      },
      "outputs": [
        {
          "data": {
            "text/plain": [
              "(np.int64(1225), np.int64(0))"
            ]
          },
          "execution_count": 139,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "np.sum(X), np.prod(X)\n",
        "# class.method(self)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "g_6eWJYmcft8"
      },
      "source": [
        "The way to understand the “axis” of numpy sum is it collapses the specified axis. So when it collapses the axis 0 (row), it becomes just one row and column-wise sum."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "id": "yGO-EguJcpLP",
        "outputId": "c5b6601c-7fa2-4f3f-8b25-7940a0485cc4"
      },
      "outputs": [
        {
          "data": {
            "text/plain": [
              "array([ 10,  35,  60,  85, 110, 135, 160, 185, 210, 235])"
            ]
          },
          "execution_count": 140,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "# sum of the rows\n",
        "np.sum(X, axis=1)"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "id": "EnhMNdmTmYhF",
        "outputId": "6fbc2ca6-b90a-4b47-8491-229cd4dd0ba6"
      },
      "outputs": [
        {
          "data": {
            "text/plain": [
              "array([ 10,  35,  60,  85, 110, 135, 160, 185, 210, 235])"
            ]
          },
          "execution_count": 141,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "X.sum(axis=1)"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "AAQ2oeiOcpLP"
      },
      "outputs": [
        {
          "data": {
            "text/plain": [
              "(np.int64(0), np.int64(49))"
            ]
          },
          "execution_count": 142,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "np.min(X), np.max(X)"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 144,
      "metadata": {
        "id": "DTxiE53y8wMh"
      },
      "outputs": [
        {
          "data": {
            "text/plain": [
              "array([[ 0,  1,  2,  3,  4],\n",
              "       [ 5,  6,  7,  8,  9],\n",
              "       [10, 11, 12, 13, 14],\n",
              "       [15, 16, 17, 18, 19],\n",
              "       [20, 21, 22, 23, 24],\n",
              "       [25, 26, 27, 28, 29],\n",
              "       [30, 31, 32, 33, 34],\n",
              "       [35, 36, 37, 38, 39],\n",
              "       [40, 41, 42, 43, 44],\n",
              "       [45, 46, 47, 48, 49]])"
            ]
          },
          "execution_count": 144,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "X"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 145,
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "id": "0h4QuwoQcpLQ",
        "outputId": "fd09ef1b-8ce4-48c8-d8c7-23c7c3a7672f"
      },
      "outputs": [
        {
          "data": {
            "text/plain": [
              "array([0, 1, 2, 3, 4])"
            ]
          },
          "execution_count": 145,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "np.min(X, axis=0)"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 146,
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "id": "q3NwyDTgcpLQ",
        "outputId": "b96e9101-33d6-49f3-9889-223465334fd6"
      },
      "outputs": [
        {
          "data": {
            "text/plain": [
              "array([[ True,  True,  True,  True,  True],\n",
              "       [ True,  True,  True,  True,  True],\n",
              "       [ True,  True, False, False, False],\n",
              "       [False, False, False, False, False],\n",
              "       [False, False, False, False, False],\n",
              "       [False, False, False, False, False],\n",
              "       [False, False, False, False, False],\n",
              "       [False, False, False, False, False],\n",
              "       [False, False, False, False, False],\n",
              "       [False, False, False, False, False]])"
            ]
          },
          "execution_count": 146,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "Y = X < 12\n",
        "Y"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 148,
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "id": "oOQuytv-cpLQ",
        "outputId": "d8feb871-d815-45f5-c249-459c98bacc73"
      },
      "outputs": [
        {
          "data": {
            "text/plain": [
              "array([ True,  True,  True, False, False, False, False, False, False,\n",
              "       False])"
            ]
          },
          "execution_count": 148,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "np.any(Y, axis=1)"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 149,
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "id": "2QdqqgUPcpLQ",
        "outputId": "b08c721d-61f1-47ea-a0f3-e52d53101c3a"
      },
      "outputs": [
        {
          "data": {
            "text/plain": [
              "array([ True,  True, False, False, False, False, False, False, False,\n",
              "       False])"
            ]
          },
          "execution_count": 149,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "np.all(Y, axis=1)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "-_TlOPAncpLQ"
      },
      "source": [
        "All the above functions can be called on the array object directly"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 147,
      "metadata": {
        "id": "QKWetd2rcpLQ"
      },
      "outputs": [
        {
          "data": {
            "text/plain": [
              "array([45, 46, 47, 48, 49])"
            ]
          },
          "execution_count": 147,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "# instance.method(args) = class.method(instance, args)\n",
        "X.max(axis=0)"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 150,
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "id": "soXpCOcWcpLR",
        "outputId": "71dba770-7426-47b1-8f72-9702b69aba1a"
      },
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "[0 1 2 3 4 5 6 7 8 9]\n"
          ]
        },
        {
          "data": {
            "text/plain": [
              "array([ 0,  1,  3,  6, 10, 15, 21, 28, 36, 45])"
            ]
          },
          "execution_count": 150,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "# np.cumsum\n",
        "Y = np.arange(10)\n",
        "print(Y)\n",
        "np.cumsum(Y)"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 151,
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "id": "OyhUdMg-cpLS",
        "outputId": "9d98fb6a-a014-46f7-8036-a05402ecdf90"
      },
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "[[ 0  1  2  3]\n",
            " [ 4  5  6  7]\n",
            " [ 8  9 10 11]\n",
            " [12 13 14 15]]\n"
          ]
        },
        {
          "data": {
            "text/plain": [
              "array([[ 0,  1,  3,  6],\n",
              "       [ 4,  9, 15, 22],\n",
              "       [ 8, 17, 27, 38],\n",
              "       [12, 25, 39, 54]])"
            ]
          },
          "execution_count": 151,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "X = np.arange(16).reshape(4, 4)\n",
        "print(X)\n",
        "np.cumsum(X, axis=1)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "yLszNjIkcpLS"
      },
      "source": [
        "Cumulative operations don't change the shape of the array"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "kLuovJTJcpLS"
      },
      "source": [
        "### Exercise (Post Lecture)\n",
        "\n",
        "- Find the column with maximum column sum\n",
        "- For which rows of the matrix, the sum of the first three elements of the row is greater than the sum of the last two elements of the row"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 156,
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "[ 8 10 16 13 15]\n",
            "2\n",
            "[0 1]\n"
          ]
        }
      ],
      "source": [
        "matrix=np.array([[1,5,7,6,2],[4,1,7,3,5],[3,4,2,4,8]])\n",
        "col_sums=matrix.sum(axis=0) \n",
        "max_col_index=np.argmax(col_sums)\n",
        "print(col_sums)\n",
        "print(max_col_index)\n",
        "first_three_sum = np.sum(matrix[:, 0:3], axis=1) \n",
        "last_two_sum = np.sum(matrix[:, 3:5], axis=1)\n",
        "\n",
        "condition = first_three_sum > last_two_sum\n",
        "print(np.where(condition)[0] )"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "d0nRCLdmcpLT"
      },
      "source": [
        "### Exercise (Post Lecture)\n",
        "Compute the the moving average of the array `y` created below, with window size 5."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 20,
      "metadata": {
        "id": "_lmcNe7ucpLV"
      },
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "[ 6.73340992e-02 -7.36435588e-03 -4.66006279e-02 -6.04604044e-02\n",
            " -1.90258790e-01 -4.02090615e-01 -5.21809806e-01 -4.90462901e-01\n",
            " -6.10432484e-01 -6.61785783e-01 -5.68282852e-01 -4.89346671e-01\n",
            " -4.56738096e-01 -3.66543960e-01 -1.95233453e-01 -4.35319811e-02\n",
            "  9.41731508e-02  1.15961229e-01  1.54042245e-01  1.43395160e-01\n",
            "  7.81995529e-02 -8.70212898e-03 -4.84424711e-04  3.56927761e-02\n",
            "  6.71226902e-02  1.30393760e-01  2.67906671e-01  3.86195616e-01\n",
            "  5.83586689e-01  7.14745544e-01  7.43788477e-01  6.71926783e-01\n",
            "  5.18532531e-01  3.49144767e-01  1.25289050e-01  8.59889236e-03\n",
            " -5.87140453e-02  1.50335438e-02 -3.71656244e-02  7.26260040e-02\n",
            "  1.27490731e-01  2.23767764e-01  2.15379740e-01  3.43989504e-01\n",
            "  3.66331535e-01  3.51346218e-01  2.63937211e-01  1.36027938e-01\n",
            " -4.44749957e-03 -1.79794861e-01 -3.11322512e-01 -3.32503869e-01\n",
            " -1.74852207e-01 -9.01607661e-02  1.29524839e-01  3.22662382e-01\n",
            "  4.46422111e-01  5.53223098e-01  6.75674765e-01  7.50942136e-01\n",
            "  7.83791034e-01  7.68601953e-01  5.88769139e-01  3.63116718e-01\n",
            "  8.55678875e-02 -4.50712374e-02 -2.39154872e-01 -3.63262690e-01\n",
            " -4.85477116e-01 -5.46537177e-01 -6.93093866e-01 -8.39022926e-01\n",
            " -8.94867387e-01 -8.61049105e-01 -7.78909723e-01 -6.82537942e-01\n",
            " -4.73670119e-01 -3.38092006e-01 -2.35594681e-01 -2.56273552e-01\n",
            " -3.00279990e-01 -4.60321985e-01 -6.62444792e-01 -9.35719755e-01\n",
            " -1.14992221e+00 -1.31119753e+00 -1.38252635e+00 -1.45590809e+00\n",
            " -1.56485479e+00 -1.58955330e+00 -1.65941164e+00 -1.72528671e+00\n",
            " -1.65430523e+00 -1.50801920e+00 -1.49384243e+00 -1.44364784e+00]\n"
          ]
        },
        {
          "data": {
            "text/plain": [
              "[<matplotlib.lines.Line2D at 0x1cb11539e50>]"
            ]
          },
          "execution_count": 20,
          "metadata": {},
          "output_type": "execute_result"
        },
        {
          "data": {
            "image/png": "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QLwAAAAAwFRAvLjJE088QcXFxRQbG0uLFy+mzZs3631Ipmf16tV06qmnijTk7Oxsuvjii+nQoUM239Pb20u33HILZWRkUGJiIl166aVUX1+v2zEHC7/61a9EAvXtt9+u3ofn2necOHGCrr76avFcxsXF0ezZs2nr1q3q4zwncf/991NeXp54fPny5VRWVqbrMZuRoaEh+ulPf0qTJk0Sz+PkyZPpoYcestmNg+faezZs2EBf//rXReItv1+88cYbNo+789y2tLTQVVddJcLrUlNT6frrr6fOzs5xHNXIXw7G4OWXX7ZER0dbnn76acu+ffssN9xwgyU1NdVSX1+v96GZmhUrVlieeeYZy969ey07d+60XHDBBZaJEydaOjs71e+56aabLIWFhZZ169ZZtm7dajn99NMtS5cu1fW4zc7mzZstxcXFljlz5lhuu+029X48176hpaXFUlRUZLn22mstmzZtslRUVFjef/99S3l5ufo9v/rVrywpKSmWN954w7Jr1y7LN77xDcukSZMsPT09uh672Xj44YctGRkZlnfeecdy9OhRy6uvvmpJTEy0PPbYY+r34Ln2nnfffdfy4x//2PLaa6+xGrS8/vrrNo+789yuXLnSMnfuXMuXX35p+fTTTy1TpkyxXHnllZbxAvHiBqeddprllltuUb8eGhqy5OfnW1avXq3rcQUbDQ0N4gXyySefiK9bW1stUVFR4g1JcuDAAfE9Gzdu1PFIzUtHR4dl6tSplrVr11rOPvtsVbzgufYd//3f/20588wznT4+PDxsyc3Ntfzv//6veh8//zExMZa///3vATrK4ODCCy+0fO9737O575JLLrFcddVV4jaea99hL17ceW73798vfm7Lli3q9/z73/+2hIWFWU6cODGu40HbaAz6+/tp27Ztohym3Z/EX2/cuFHXYws22traxHV6erq45ud9YGDA5rkvLS2liRMn4rn3Em4LXXjhhTbPKYPn2ne89dZbtGjRIrrssstEO3T+/Pn01FNPqY8fPXqU6urqbJ5r3ufC7Wg8156xdOlSWrduHR0+fFh8vWvXLvrss8/o/PPPF1/jufYf7jy3fM2tIn49SPj7+TN006ZN4/r7g24xo69pamoSfdWcnByb+/nrgwcP6nZcwQZvA2f/xRlnnEGzZs0S9/ELIzo6Wvzy2z/3/BjwjJdffpm2b99OW7ZsGfUYnmvfUVFRQX/605/ozjvvpPvuu0883z/84Q/F8/vd735XfT4dvafgufaMe++9V2w0ZqEdEREh3qsffvhh4bFg8Fz7D3eeW75mAa8lMjJSnKCO9/mHeAGGqQjs3btXnDUB38Or6m+77TZau3atMJ0D/wpxPtP85S9/Kb7mygv/bq9Zs0aIF+A7/vGPf9CLL75IL730Ep1yyim0c+dOcRLEBlM818EN2kZjkJmZKRS9/dQFf52bm6vbcQUTt956K73zzjv08ccfU0FBgXo/P7/ctmttbbX5fjz3nsNtoYaGBlqwYIE48+HLJ598Qr///e/FbT5bwnPtG3jyYubMmTb3zZgxgyorK8Vt+XziPWX83H333aL6csUVV4iJru985zt0xx13iElGBs+1/3DnueVrft/RMjg4KCaQxvv8Q7yMAZd6Fy5cKPqq2jMr/nrJkiW6HpvZYQ8YC5fXX3+dPvroIzHuqIWf96ioKJvnnkep+UMAz71nLFu2jPbs2SPOTOWFqwNcXpe38Vz7Bm592o/8syejqKhI3Obfc37j1j7X3PpgDwCea8/o7u4W/gktfLLJ79EMnmv/4c5zy9d8QsQnTxJ+r+d/H/bGjItx2X1DaFSaHdTPPvuscE/feOONYlS6rq5O70MzNTfffLMYs1u/fr2ltrZWvXR3d9uM7/L49EcffSTGd5csWSIuYPxop40YPNe+G0WPjIwUY7xlZWWWF1980RIfH2954YUXbEZM+T3kzTfftOzevdty0UUXYXzXC7773e9aJkyYoI5K80hvZmam5Z577lG/B8/1+KYTd+zYIS4sF37729+K28ePH3f7ueVR6fnz54vYgM8++0xMO2JUOoA8/vjj4o2d8154dJpn1sH44BeDowtnv0j4RfBf//VflrS0NPEB8M1vflMIHOB78YLn2ne8/fbbllmzZomTntLSUsuTTz5p8ziPmf70pz+15OTkiO9ZtmyZ5dChQ7odr1lpb28Xv8P83hwbG2spKSkRuSR9fX3q9+C59p6PP/7Y4Xs0i0Z3n9vm5mYhVjh/Jzk52XLdddcJUTRewvg/46vdAAAAAAAEDnheAAAAAGAqIF4AAAAAYCogXgAAAABgKiBeAAAAAGAqIF4AAAAAYCogXgAAAABgKiBeAAAAAGAqIF4AAAAAYCogXgAAAABgKiBeAAAAAGAqIF4AAAAAYCogXgAAAABAZuL/AxQHf00ZaZYfAAAAAElFTkSuQmCC",
            "text/plain": [
              "<Figure size 640x480 with 1 Axes>"
            ]
          },
          "metadata": {},
          "output_type": "display_data"
        }
      ],
      "source": [
        "import numpy as np\n",
        "import matplotlib.pyplot as plt\n",
        "\n",
        "np.random.seed(10)\n",
        "y = np.cumsum(np.random.rand(100) - 0.5)\n",
        "moving_avg = np.convolve(y, np.ones(5)/5, mode='valid')\n",
        "print(moving_avg)\n",
        "plt.plot(y)"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 23,
      "metadata": {
        "id": "zzs-dYZAcpLW"
      },
      "outputs": [],
      "source": [
        "\n",
        "\n",
        "\n",
        "# plt.plot(y)\n",
        "# plt.plot(z)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "o2s_gqSucpLW"
      },
      "source": [
        "## Final Exercise (Post Lecture)\n",
        "\n",
        "Implement the [Conway's game of Life](https://en.wikipedia.org/wiki/Conway%27s_Game_of_Life) using numpy"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 41,
      "metadata": {
        "id": "_QD_KrJ_cpLX"
      },
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "--- 康威生命游戏示例 ---\n",
            "\n",
            "模拟随机网格...\n"
          ]
        },
        {
          "data": {
            "image/png": "iVBORw0KGgoAAAANSUhEUgAAAYUAAAGbCAYAAAAr/4yjAAAAOnRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjEwLjYsIGh0dHBzOi8vbWF0cGxvdGxpYi5vcmcvq6yFwwAAAAlwSFlzAAAPYQAAD2EBqD+naQAAI7VJREFUeJzt3QeQJVXZxvG+sCTJUfKSJEmOkiUjIEFARZGgpBIEyUEBAUmCBEGSloiIFEEURERUoGDBUhFQKRQUEUSisATJob96zld9quduz0zPmZP67v9XNQU7c2/H0/326feEXlmWZQEAQFEU06TeAABAPggKAACLoAAAsAgKAACLoAAAsAgKAACLoAAAsAgKAACLoAAAsAgKmKo8++yzxc4771zMPffcRa/XK84999xxLe+jH/2o+Qm5Dri74447zDnQfzEgQeHRRx8t9ttvv2KJJZYoZpxxxmK22WYr1ltvveK8884r3njjjWLQ7bnnnlPcdMbjrbfeKs4///xi/fXXL+acc85i+umnLxZccMFiu+22K6666qrivffeKwbZIYccUvzyl78sjjnmmOKKK64ottpqq2E/q5vJgQceGHQdId11113FJz/5yWKhhRYy53n22Wcv1l577eKkk04ygWuQXHjhhcX3v//9Ijd//etfzfmfZZZZirnmmqv43Oc+Vzz//PNFzno5j33085//vNhll12KGWaYodh9992LFVZYoXj77beLSZMmFT/+8Y/NDfPSSy8tBpn28V//+peXJx0Vxo997GPFH//4x2LLLbcsNt98c1NQn3nmmeLXv/51cdttt5kbxnHHHVcMqvnnn7/YbLPNih/+8IejflZB4YADDiguuOCCYT+j8ii66bqsI5Tjjz++OPnkk83D1Kc//Wnz3zfffNOce10788wzj3ngGhS6N2if+q+T999/35wjnZ9ppon7DPzkk08Wq666qgnGBx10UPG///2vOOuss4pFF120+P3vfz+kzORkQpGpxx57zBTmiRMnmpvVAgssYP+mC/Uf//iHCRpoT08p999/v7kpfOITnxjyNz3V3nvvvcXDDz9cDLLnnnuumGOOObwtr+nC9r2Osbr66qtNQFAtQTWV/m0855xzzE+u9JyqADbTTDONe1kKBHrDkMKpp55avPbaayYQKxDIWmutZR7GVKvZd999iyyVmdp///1VgynvvvvuVp9/5513ypNOOqlcYoklyumnn76cOHFiecwxx5RvvvnmkM/p99tss0151113lWuuuWY5wwwzlIsvvnh5+eWX289Mnjy5nGaaacrzzjvP/u75558ve71eOddcc5Xvv//+kO384Ac/aP995513ljvvvHO5yCKLmO1YeOGFyy9/+cvl66+/bj/zve99z+zbfffdN8V+nHLKKWbdTz75pPn3HnvsUW600UZDPnPVVVeVq622WjnLLLOUs846a7nCCiuU55577ojH55577jHr1Pa29dZbb5XHHXecWddss81WfuADHyjXX3/98rbbbhvyuccee8ws+8wzzywvuOACczxnmmmmcvPNNy+feOIJc7x0bhZaaKFyxhlnLLfbbrvyhRdemGJ9N998s1m+1qN923rrrcsHH3yw1bY++uij5rjPOeecZt1rr712edNNN9m/X3bZZWYb+39Gor8fcMABI35G56Y6P6OtQ+Xq4IMPNmVCZWPJJZcsTz/99PK9994rfVp66aXLeeaZp3z11VfH9L02x1/lceaZZzblc/vttzf/r3Uddthh5bvvvjvks9qvc845p1x++eXNdTbffPOV++67b/niiy82XpO33HJLufrqq5vP6nvVtbLxxhuX8847rzlmyy23XHnhhRdO8f3+Y16dk9tvv938W/+tu+aaa0y5Vnmce+65y89+9rP2mnPZ1yba31122aXx/Gy66aZlrrINCrqB6Abflk6gTr5uDN/+9rfL3Xff3fx7hx12mKIALbPMMuZGfuyxx5qbmAqHbvj1C2CllVYqd9ppJ/vvn/zkJ+ZmrWXWP/fhD3/YrLPypS99yVxMp556annJJZeUX/jCF8ppp512yGdeeeUVc+NS4eqnC2iTTTYZsl/1oHDrrbeabVCh0n7q58ADD2wsfHUKkPrepEmTWh7R/w+ECyywQHnooYeWF110UfmNb3zDHLvpppuuvP/++6cICqussorZ/rPPPrv86le/ai7ij3zkI+Y4r7vuuuW3vvWt8qCDDjLHeq+99hqyrh/84Afm91tttVV5/vnnl2eccUa52GKLlXPMMYdZ/kieeeYZcz4VIL/yla+Y9a+88srmfF1//fU2aFxxxRVmOxWs9P/68RkURlrHa6+9ZsqUbkA6HhdffLEpo9pnBQpfHn74YbP+vffee0zfa3v8VR51I1W5//znP2/Kha4TrbP/Zq1tmDBhQrnPPvuY/T3qqKPMjVUPY2+//faQa3KppZYyAf3oo482n61u4vrsnnvuaYKEtmuLLbYw69J1W782FWiXXXZZe8x1nQwXFKrgrWVruVqnrkft7+TJk532tZ8CiT6n49hvt912Mw+XucoyKLz88svmgCo6t/HAAw80XgiHH364+X39ybZ6qtATfeW5554zTyf1m7RuBvUagG6MG264oYn+Khyip11dSPUaRb1GUDnttNPM5x5//HH7u1133bVccMEFhzwlquagbVOhHY5uIHpqb/OkUrfjjjuaZb/00ktDfv/GG2+Ym3/1U78otA7VFur0dx0XXST9QUFPc/XlV4FIN2jV5Or7roBR1eL0RKubj24e/Tf72WeffYrf91NNTOtR7a+iZarGogu9fozb3Ohdg8JI3zv55JPNDfGRRx4Z8nvdkPTQoBqVDzfccINZf3/NUbW1+nnWT3VOxnL8q4cv1fzqVl11VfOUX9G50OeuvPLKIZ9TbaD/99U1qb/1a7qettxyyykeGHXj7j8PTUFBwUjXsGrXKvsV1Sr1ueOPP37M+9rkD3/4g/mugm2/I444wvyt/y1GLrJsffTKK6+Y/84666ytPn/zzTeb/x566KFDfn/YYYeZ//bnHpZffvligw02sP+ed955i2WWWab45z//aX+nv6uFRvWOXS05NtxwQ/N7/b8o4a17QH1Z9fegep/43//+t1h33XXN5/Q+v6LE+VNPPVXcfvvt9ndXXnml+f5OO+007L7qXbWW+6tf/apwOaZqBVF38cUXm/2vftQqqTLttNPa99FK2L344ovFu+++W6yxxhrFfffdN8U61ChASbWKWrrIbrvtVkyYMGHI75X8+89//mP+rX156aWXil133dUcr+pH69dn68douPOvd7X1bdd+6p2tkvQPPfRQkdq1115ryolafNX3UQlptfi68847vaxnuPP88ssvDznP+nnggQecj//+++8/5N/at/r1o/1VWdD78/oyV199dbNt/ctcfPHFTeOHfvXrSfugZWy00UZmXfr3WClvppzPF7/4xSG5hm222aZYdtllG/OUo+1rk6plpBrJ9KvWm2vrySwTzWp2Kq+++mqrzz/++OMmobTUUksN+b1agegmqr/XVUmfOl2skydPtv+ubvQKAAsvvLC5oX/96183F5NaEFR/07auvPLK9ntPPPGEaflx4403Dlme1AuxLhYlzxUINt10U3PTVZPQ7bfffsRgqMJ8zTXXmFZEamq4xRZbmITiaM0eq2WqBUT9xq0ApJYbVRDtb5J6+eWXF9/85jeLv/3tb8U777wz5CLu139cq/Usssgijb+vjs/f//53899NNtlkxPIwHJ3fKgDVLbfccvbv1T6mon3885//bMpPE92ohqNyU7+BKFCr1dho57lON+LqQeLWW28tzjzzzCHbNpbjr5ta/370Xz9aprZ7vvnma7W/TeVJ7r777uKEE04ofvvb3xavv/76kL9p+fWy3EZ1L9BDYD8FhUmTJo15X5tUwUxNwPspiV7/TG6yDQpqO//ggw+O6XtqQtiGnoCa1Fvnav0qqHqCW2yxxczf1llnHVNADj74YFO4FBRUC6iauumGqpu9nqiPOuooU8hmnnlm80SspqW68de34TOf+Uzxne98x7SxVuFXzUFP1SPRRaYnPLWD/8UvfmF+LrvsMlPz0A18ONoW0TFVP4+KbtjVTbt6iq2oSaW2e4cddiiOOOIIs25t92mnndbYnHG44zra8a6Oi1rKKJD3q9cyukr7qLJx5JFHNv596aWXHva7Km/1c6sn5eGaKNfPc/8xVK2kairZv21jOf7Dnc/+Zaq86KGnSf+NtukGqTKmBybt09lnn23KqQKiaoZqPVW/nkKZtsW+NqlaSz799NNT/E2/U1BvqkXkINurbdtttzV9EPSEoJvxSNRsVQVETyfV06Ho9Y+qxfq7C9UWFBQUHFZZZRXzFKZagZ5ObrnlFvMK5cQTT7Sf/8tf/lI88sgj5gLWTboy3KsefUZP4T/72c/MzV0XSlMVup8ujI9//OPmR/ut2sMll1xi+hf015bqx/P00083F2k9KIzkuuuuM+3br7/++iEBV09uPi255JLmv7qJVDeusdD5bWpKq9pN9ffUtI96enfZPwWS+sOCgvdw9AT8oQ99qPjpT39qelLroaTNto3n+A+3TPV9UVlzfSLWdaEnbdW667XQptdZbR8Iq7Kg8tJfM9LvJnoqK6rF63rW66p+6qOg+0musswpVBeCCvTee+/d2PtSTxHq1Sxbb721+W//cAJ6uqjeF7oGBb2TVrvv6nWSagWqHWjZep1SzydUTxX1Gof+v9rOfiuttJL5+e53v2v6DqhfxmhPxS+88MKQf2t7tIzhqqoVXZx6UlWgveGGGxo/09+PsWl/fve735lA7ZMCoWqHatddf0VVGa0HqM6/LrT6dinvon1VLU85pNT0ik/bpxpePz24KFczHG2/btbVj97Lj+RrX/uaqfHts88+jcez/zyP9/gPt7+qOau/RD/tq/Z5NE3lT6+MVDPup3tFm2UqH6bgp1xa/XrRQ5l6H2/jeK9oolezN910U/Hvf//b/u43v/mNeXBU/i1X2dYU9KTxox/9qPjUpz5lnv7rPZrvuecek8jSqw3R0/see+xhbgIqGKpe6yahJ3a9+th4442dtqG64esJQhdMRQlnFSJV/9Zcc037e1Vztd2HH364eWWkC003+5HeP2q/9HkZ7dWRKEjq9ZSecpTr0GssDVuhJ496LamJXgcp96BjopyEbjB66qx6NKtWpN/XaxeqJey4447mYlGHQl1Mukn1v7MeDx2niy66yHSuW2211Uxw1FOW8jNK/CmgjdSr+Oijjzb5GG27eo6qaq5zr+3V8R9PT1Y96SmX1E9Dj9QT26PR6zc98eqYqtzqxq7ApdqlamR6+FCPXB/0WlKvj/SaT9eBjqdqu1qffq9jpVpvVeMY7/FvomtQw9NoG/S6U7mv6aabztTmde3qQUnjQ41E36lqxVqWypxet+qm3v9aRsdT+6BzpdqyPtOUI9E2nHHGGcVee+1ltlHJdT10anv0AHHIIYcUvhx77LFmX3X/0StAbb9yOSuuuKJZf7bKzKkJn5rEqWmhmjGqLfp6661n2izXm3Sped2JJ55omiGqHb06j43Uea1N00JR8zUdpmeffdb+Tm399bsNNthgis8/9NBD5WabbWY6/6iji7b9T3/607BNTZ9++mnTJFEdWtq47rrrTFttbZeOx6KLLlrut99+ZjltqBmemiuus846pmmr2pHPP//85bbbbmuaCdabuqoZo/pb6Jipya6a4qnpnprq6XdNndeamgNee+21Q35ftRNXs73+z6u5oZpBqn24Onepjfq9997buvOamlbqu2uttdaQzmuuTVKH+1ET07E0Sa2afqpMqk2+zp3Kh/pvnHXWWUPa7ftyxx13mGOivia6JnS+11hjjfKEE05oLC9tjn/Voaufltl0O7n00ktN8031A9C1u+KKK5ZHHnlk+dRTT416TcqNN95o+ndoe3QPULv/qvNnvf+Ems9qGVpHm85r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",
            "text/plain": [
              "<Figure size 640x480 with 1 Axes>"
            ]
          },
          "metadata": {},
          "output_type": "display_data"
        }
      ],
      "source": [
        "import numpy as np\n",
        "import time\n",
        "import matplotlib.pyplot as plt\n",
        "import matplotlib.animation as animation\n",
        "def conway_game_of_life(initial_grid, generations, visualize=False, interval_ms=200):\n",
        "    grid = initial_grid.copy()\n",
        "    rows, cols = grid.shape\n",
        "    if visualize:\n",
        "        fig, ax = plt.subplots()\n",
        "        img = ax.imshow(grid, cmap='binary', interpolation='nearest')\n",
        "        ax.set_title(f\"Conway's Game of Life - Generation 0\")\n",
        "        plt.axis('off')\n",
        "        history = [grid.copy()]\n",
        "    for gen in range(generations):\n",
        "        padded_grid = np.pad(grid, 1, mode='constant', constant_values=0)\n",
        "        neighbors = (\n",
        "            padded_grid[0:rows, 0:cols] + \n",
        "            padded_grid[0:rows, 1:cols+1] +   \n",
        "            padded_grid[0:rows, 2:cols+2] +   \n",
        "            padded_grid[1:rows+1, 0:cols] +   \n",
        "            padded_grid[1:rows+1, 2:cols+2] + \n",
        "            padded_grid[2:rows+2, 0:cols] +  \n",
        "            padded_grid[2:rows+2, 1:cols+1] + \n",
        "            padded_grid[2:rows+2, 2:cols+2]   \n",
        "        )\n",
        "        new_grid = np.zeros_like(grid)\n",
        "        new_grid[(grid == 1) & ((neighbors == 2) | (neighbors == 3))] = 1\n",
        "        new_grid[(grid == 0) & (neighbors == 3)] = 1\n",
        "        grid = new_grid\n",
        "\n",
        "        if visualize:\n",
        "            history.append(grid.copy())\n",
        "\n",
        "    if visualize:\n",
        "        def update(frame):\n",
        "            img.set_array(history[frame])\n",
        "            ax.set_title(f\"Conway's Game of Life - Generation {frame}\")\n",
        "            return [img]\n",
        "\n",
        "        ani = animation.FuncAnimation(fig, update, frames=len(history),\n",
        "                                      interval=interval_ms, blit=True, repeat=False)\n",
        "        plt.show()\n",
        "\n",
        "    return grid\n",
        "def create_random_grid(size=(50, 50), live_density=0.2):\n",
        "    return np.random.choice([0, 1], size=size, p=[1 - live_density, live_density])\n",
        "if __name__ == \"__main__\":\n",
        "    print(\"--- 康威生命游戏示例 ---\")\n",
        "    print(\"\\n模拟随机网格...\")\n",
        "    random_initial_grid = create_random_grid(size=(80, 80), live_density=0.25)\n",
        "    final_random_grid = conway_game_of_life(random_initial_grid, generations=150, visualize=True, interval_ms=100)"
      ]
    }
  ],
  "metadata": {
    "colab": {
      "provenance": [],
      "toc_visible": true
    },
    "kernelspec": {
      "display_name": "base",
      "language": "python",
      "name": "python3"
    },
    "language_info": {
      "codemirror_mode": {
        "name": "ipython",
        "version": 3
      },
      "file_extension": ".py",
      "mimetype": "text/x-python",
      "name": "python",
      "nbconvert_exporter": "python",
      "pygments_lexer": "ipython3",
      "version": "3.13.5"
    }
  },
  "nbformat": 4,
  "nbformat_minor": 0
}
